package com.nineclock.auth.intergration.aop;

import afu.org.checkerframework.checker.oigj.qual.O;
import com.aliyun.oss.common.comm.RequestChecksumHanlder;
import com.nineclock.auth.intergration.entity.IntergrationAuthenticationEntity;
import com.nineclock.auth.intergration.entity.NcOauthTokenDTO;
import com.nineclock.auth.intergration.threadlocal.IntergrationAuthenticationHolder;
import com.nineclock.auth.intergration.threadlocal.UserHolder;
import com.nineclock.common.entity.Result;
import com.nineclock.common.entity.UserInfo;
import com.nineclock.common.enums.ResponseEnum;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * AOP
 *
 * @author zhangyan
 * @date 2021/4/1 21:12
 */

@Component
@Aspect //把当前类标识为一个切面供容器读取
@Slf4j
public class AuthTokenAspect {

    /**
     * 环绕增强,拦截 TokenEndPoint 中申请令牌的方法
     *
     * @param proceedingJoinPoint
     * @return
     */
    @Around("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")
    //切入点表达式
    public Object postHandleAuthTokenResult(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        log.info("进入AOP切面,拦截申请令牌的方法");
        Object result = null;
        Result res = null;

        try {
            //1.获取请求参数
            /*
            spring提供的,通过RequestContextHolder 在任意地方获取Request对象
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
            */

            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.getRequestAttributes())).getRequest();
            //获取auth_type
            String authType = request.getParameter("auth_type");
            //获取其他参数
            Map<String, String[]> parameterMap = request.getParameterMap();

            //2.组装认证实体
            IntergrationAuthenticationEntity authenticationEntity = new IntergrationAuthenticationEntity();
            authenticationEntity.setAuthType(authType);
            authenticationEntity.setAuthParameters(parameterMap);

            //3.存入ThreadLocal
            IntergrationAuthenticationHolder.set(authenticationEntity);



            //4.放行 --> 去执行原始方法了 (返回结果是 ResponseEntity< OAuth2AccessToken >)
            result = proceedingJoinPoint.proceed();



            //-------------------------------后置增强---------------------------------------

            //统一响应结果
            ResponseEntity<OAuth2AccessToken> responseEntity = (ResponseEntity<OAuth2AccessToken>) result;

            if (responseEntity != null){
                //获取令牌封装对象
                OAuth2AccessToken auth2AccessToken = responseEntity.getBody();
                //从ThreadLocal取出userInfo
                UserInfo userInfo = UserHolder.get();

                if (responseEntity.getStatusCode().is2xxSuccessful()){
                    //响应码2xx,成功,组装令牌
                    NcOauthTokenDTO oauthTokenDTO = new NcOauthTokenDTO();
                    oauthTokenDTO.setUser(userInfo);
                    oauthTokenDTO.setOauth2AccessToken(auth2AccessToken);
                    //封装到res
                    res = Result.success(oauthTokenDTO);

                }else {
                    Result.errorMessage(ResponseEnum.INVALID_USERNAME_PASSWORD.getMessage());
                }
            }

        }finally {
            //释放ThreadLocal中存储的认证实体信息
            IntergrationAuthenticationHolder.remove();
        }
        //5.返回
        //return result;
        //环绕增强的返回值要与原方法的返回值类型一致 TokenEndpoint()-->ResponseEntity<>
        return ResponseEntity.ok(res);
    }
}
